/**
 * 
 */
package com.pxingg.fep.serial;

import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.pxingg.exception.XException;
import com.pxingg.fep.CommPort;

/**
 * 打开指定串口，监听"com.pxingg.communication.serial." + aliasName + ".out"中的消息，把消息中的数据转换成binary数据发送出去。<br>
 * 把串口的输入，格式化成Message消息，发布到"com.pxingg.communication.serial." + aliasName + ".in"上。
 * 
 * @author pxingg
 * 
 */
public class Serial implements CommPort
{
    /**
     * The log of this class
     */
    static Log log = LogFactory.getLog(Serial.class);
    /**
     * 从执行打开串口方法，到计算机真正打开串口需要的时间(单位ms)
     */
    private static final int DEFAULT_SERIAL_INIT_TIME = 3000;
    /**
     * 默认的串口数据传输速率X bit/s
     */
    private static final int DEFAULT_SERIAL_BIT_RATE = 9600;

    /**
     * 获取当前系统上可以使用的串口(只会列出串口)<br>
     * 返回的结果集中的值，可以在下面函数中使用<br>
     * {@link #connect(String, int)},{@link #connect(String)}<br>
     * 用了打开指定的串口
     * 
     * @return 一个包含{@link String}类型的{@link Vector}，包含所有可用的串口的名字
     */
    @SuppressWarnings("unchecked")
    public static Vector<String> getPortList()
    {
        Enumeration<CommPortIdentifier> portList;
        final Vector<String> portVect = new Vector<String>();
        portList = CommPortIdentifier.getPortIdentifiers();

        CommPortIdentifier portId;
        while (portList.hasMoreElements())
        {
            portId = portList.nextElement();
            System.out.println(portId.getName());
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
            {
                portVect.add(portId.getName());
            }
        }

        // begin to write the log list to the object
        final StringBuffer buffer = new StringBuffer("found the following ports: ");
        for (int i = 0; i < portVect.size(); i++)
        {
            if (i != 0)
            {
                buffer.append(", ");
            }
            buffer.append(portVect.elementAt(i));
        }

        Serial.log.info(buffer.toString());

        return portVect;
    }

    /**
     * 给当前串口取一个名字
     */
    private final String aliasName;
    /**
     * 在操作系统上，这个端口对应的文件名,为{@link #getPortList()}得到的一个值
     */
    private final String portName;
    /**
     * 当前串口数据传输速率 X bit/s
     */
    private final int speed;
    /**
     * 当前串口是否已经连接成功
     */
    private boolean connected = false;
    /**
     * 当前操作的串口
     */
    private SerialPort serialPort = null;

    /**
     * 串口所使用到的输入流
     */
    private InputStream inputStream = null;
    /**
     * 串口所使用到的输出流
     */
    private OutputStream outputStream = null;


    /**
     * 
     * @param aliasName
     *            当前串口的名字
     * @param portName
     *            系统上串口的文件名，为{@link #getPortList()}得到的一个值
     */
    public Serial(final String aliasName, final String portName)
    {
        this.aliasName = aliasName;
        this.portName = portName;
        speed = Serial.DEFAULT_SERIAL_BIT_RATE;
    }

    /**
     * 
     * @param aliasName
     *            当前串口的名字
     * @param portName
     *            系统上串口的文件名，为{@link #getPortList()}得到的一个值
     * @param speed
     *            串口的通信速度
     */
    public Serial(final String aliasName, final String portName, final int speed)
    {
        this.aliasName = aliasName;
        this.portName = portName;
        this.speed = speed;
    }

    @Override
    public int available() throws XException
    {
        int result = 0;
        if(inputStream != null)
        {
            try
            {
                result = inputStream.available();
            }
            catch (final IOException e)
            {
                // 出现异常时关闭端口
                close();
                Serial.log.error(e);
                throw new XException(e.getCause());
            }
        }

        return result;
    }

    @Override
    public void close() throws XException
    {
        serialPort.close();
        inputStream = null;
        outputStream = null;
        connected = false;
    }

    @Override
    public void flush() throws XException
    {
        if(outputStream != null)
        {
            try
            {
                outputStream.flush();
            }
            catch (final IOException e)
            {
                // 出现异常时关闭端口
                close();
                Serial.log.error(e);
                throw new XException(e.getCause());
            }
        }
    }

    @Override
    public String getAliasName()
    {
        return portName  + "(" + aliasName + "-" + speed + ")";
    }

    @Override
    public boolean isClose()
    {
        return ! isOpen();
    }


    @Override
    public boolean isOpen()
    {
        return connected;
    }

    @Override
    public CommPort open() throws XException
    {
        if (connected)
        {
            // 不在重复进行打开端口的操作
            return this;
        }
        CommPortIdentifier portIdentifier;
        try
        {
            portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
            if (portIdentifier.isCurrentlyOwned())
            {
                Serial.log.error(this + " is currently in use");
            }
            else
            {
                // 打开串口，并等待一段时间，直到串口能够成功处理数据
                serialPort = (SerialPort) portIdentifier.open(getClass().getSimpleName(), 2000);
                serialPort.setSerialPortParams(speed, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                Thread.sleep(Serial.DEFAULT_SERIAL_INIT_TIME);

                inputStream = serialPort.getInputStream();
                outputStream = serialPort.getOutputStream();

                // 走到这里，认为端口已经成功打开
                connected = true;

                Serial.log.info(this +  " established at the bit rate " + speed);
            }
        }
        catch (final NoSuchPortException e)
        {
            Serial.log.error(this + " connection could not be made, reason: port is not exist");
        }
        catch (final PortInUseException e)
        {
            Serial.log.error(aliasName + " port " + portName + " the connection could not be made, reason: " + e.getMessage());
            Serial.log.error(e);
        }
        catch (final UnsupportedCommOperationException e)
        {
            Serial.log.error(aliasName + " port " + portName + " the connection could not be made, reason: " + e.getMessage());
        }
        catch (final InterruptedException e)
        {
            Serial.log.error(aliasName + " port " + portName + " the connection could not be made, reason: " + e.getMessage());
            // catch the exception to close the bus
            Thread.currentThread().interrupt();
        }
        catch (final IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return this;
    }

    @Override
    public int read() throws XException
    {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int read(final byte[] data) throws XException
    {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int read(final byte[] data, final int start, final int length) throws XException
    {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public CommPort reopen() throws XException
    {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * to format the message to string
     * 
     */
    @Override
    public String toString()
    {
        return getAliasName();
    }

    @Override
    public CommPort write(final byte[] data) throws XException
    {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CommPort write(final byte[] data, final int start, final int length) throws XException
    {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CommPort write(final int data) throws XException
    {
        // TODO Auto-generated method stub
        return null;
    }
}
